/****************************************************
          File Name:html.c
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Tue 13 Jan 2015 11:16:37 AM CST
*****************************************************/

#include "header.h"
#include "pisces_dlist.h"
#include "global.h"
#include <ctype.h>

extern void http_response(abby_response resp);
char *link_s = "<tr><td><a href=\"";
char *link_e = "</a></td>";
char *info_s = "<td>";
char *info_e = "</td>";
char *tr_e = "</tr>";

void make_page(const char *body, struct html *page, bool islist)
{
    char *header = NULL;
    char *body_s;
    char *body_e;
    if (islist == true) {
        header = "<!doctype html>\n<html>\n\t<head>\n\t\t<meta charset=\"utf-8\"/>"
            "\n\t\t<title>Abby's HTTP Server</title>\n<link rel=\"stylesheet\""
            " type=\"text/css\" href=\"/style.css\">\n</head>";

        body_s = "<body>\n\t\t<table><tr><th>Name</th><th>Type</th>"
            "<th>Size</th><th>Last Modified</th></tr>\n";
        body_e = "\n\t\t</table>\n\t</body>\n</html>";
    } else {
        header = "<html>\n\t<head>\n\t\t<meta charset=\"utf-8\"/>"
            "\n\t\t<title>Abby's HTTP Server</title>\n</head>";
        body_s = "\t<body>\n\t\t<pre>\n";
        body_e = "\n\t\t</pre>\n\t</body>\n</html>";

    }
    page->html_page = (char *)malloc(strlen(header) + strlen(body_s) + strlen(body) + strlen(body_e));
    sprintf(page->html_page, "%s%s%s%s", header, body_s, body, body_e);
    page->size = strlen(page->html_page);
}

static char *upper(char *s)
{
    for (; *s != '\0'; s++)
        if (*s >= 'a' && *s <= 'z')
            *s -= 32;
    return s;
}

void parse_ext(char *filetype, const char *path)
{
    int i;
    FILE *fp;
    char *line = NULL, *tmp = NULL;
    size_t len = 0, read;

    memset(filetype, 0, strlen(filetype));
    if ((fp = fopen(MIME_PATH, "r")) != NULL) { /* if error then let filetype blank */
        for (; (read = getline(&line, &len, fp)) != -1;) {
            tmp = line;
            len = strlen(line);
            for (i = 0; !isspace(line[i]); i++) ;
            line[i] = '\0';
            if (strstr(path, line)) {
 again:
                for (; !isalpha(line[i]); i++) ;
                line += i;
                memset(filetype, 0, 32);
                strncpy(filetype, line, len - i - 1);
                break;
            }
            upper(line);
            if (strstr(path, line))
                goto again;
        }
        if (read == -1)
            strcpy(filetype, "text/plain");
        line = tmp;
        tmp = NULL;
        free(line);
        fclose(fp);
    }
}

void pisces_err(int sockfd, int code)
{
    abby_response resp;
    struct html page;
    char http_error[CONTENT_SIZE] = "<p style=\"color:red; font-size:48px; text-align:center\">";
    switch (code) {
    case 403:
        sprintf(http_error, "%s403 You have no permission</p>", http_error);
        break;
    case 404:
        sprintf(http_error, "%s404 not found. ORZ</p>", http_error);
        break;
    case 503:
        sprintf(http_error, "%s503 Abby's Server is busy now, Please try again in 20 seconds</p>",
                http_error);
        break;
    }
    make_page(http_error, &page, false);
    resp.confd = sockfd;
    resp.code = code;
    resp.mtime = 0;             /* dont't show Last-Modified */
    resp.range.offset = -1;
    resp.range.end = -1;
    resp.size = page.size;
    resp.type = "text/html";
    http_response(resp);
    if (write(sockfd, page.html_page, page.size) == -1)
        exit(EXIT_FAILURE);
}

static void build_link(char body[], const char *dir, const char *name, const char *info)
{
    if (strcmp(dir, "/") == 0)
        sprintf(body, "%s%s%s%s\">%s%s%s%s\n", body, link_s, dir, name, name, link_e, info, tr_e);
    else
        sprintf(body, "%s%s%s/%s\">%s%s%s%s\n", body, link_s, dir, name, name, link_e, info, tr_e);
}

void make_link(int sockfd, const char *rootdir, const char *dir)
{
    DIR *dirp = NULL;
    struct dirent *dp = NULL;
    char *body = NULL;
    int i = 0, ci = 0, fi = 0;
    char path[PATH_MAX];        /* file's abs path */
    unsigned long dirsize = 0, bodylen, itemlen;
    double size = 0.0, KB = 1024.0, MB = 1048576, GB = 1073741824;
    char ext = 'B';
    char info[256];
    struct stat tmp;            /* file's status */
    struct html page;
    struct tm tm;
    char date[25];
    char filepath[PATH_MAX];
    pisces_dlist_t *head_dir = NULL, *head_file = NULL, *cdir = NULL, *cfile = NULL;    /* content dir and file */
    size_t l_size = sizeof(pisces_dlist_t);
    pisces_dlist_init(&head_dir);
    pisces_dlist_init(&head_file);

    snprintf(filepath, PATH_MAX, "%s%s", rootdir, dir);
    chdir(dir);

    if ((dirp = opendir(filepath)) == NULL)
        pisces_err(sockfd, 404);
    else {
        for (; (dp = readdir(dirp));) {
            if (dp->d_name[0] != '.') {
                memset(path, 0, PATH_MAX);
                sprintf(path, "%s/%s", filepath, dp->d_name);
                if (lstat(path, &tmp) == -1) {
                    perror("stat");
                    exit(1);
                }
                if (S_ISLNK(tmp.st_mode))
                    continue;
                itemlen = strlen(dp->d_name) + 1;
                dirsize += itemlen;
                if (S_ISDIR(tmp.st_mode)) {
                    cdir = (pisces_dlist_t *) malloc(l_size);
                    if (cdir == NULL) {
                        perror("malloc");
                        exit(1);
                    }
                    cdir->item = (char *)malloc(itemlen);
                    strncpy(cdir->item, dp->d_name, itemlen);
                    cdir->item[itemlen - 1] = '/';
                    cdir->item[itemlen] = '\0';
                    cdir->mtime = tmp.st_mtime;
                    cdir->index = ci;
                    ci++;
                    pisces_dlist_insert(&head_dir, cdir);
                } else {
                    cfile = (pisces_dlist_t *) malloc(l_size);
                    if (cfile == NULL) {
                        perror("malloc");
                        exit(1);
                    }
                    cfile->item = (char *)malloc(itemlen);
                    strncpy(cfile->item, dp->d_name, itemlen);
                    cfile->mtime = tmp.st_mtime;
                    cfile->st_size = tmp.st_size;
                    cfile->index = fi;
                    fi++;
                    pisces_dlist_insert(&head_file, cfile);
                }
                i++;            /* how many items in directory */
            }
        }
    }

    bodylen = 380 * (i == 0 ? 1 : i) + dirsize + strlen(dir);   /* info*,link*,tr* + name + dir */
    body = (char *)malloc(bodylen);
    memset(body, 0, bodylen);
    if ((strlen(filepath) - 1) != strlen(rootdir))      /* hide ../ in rootdir */
        sprintf(body, "%s%s%s/%s\">%s\t%s%s\n", body, link_s, dir, "..", ". . /", link_e, tr_e);

    if (ci != 0) {
        pisces_dlist_sort(head_dir->pre, head_dir->next);
        cdir = head_dir->next;
        for (; cdir->index != -1; cdir = cdir->next) {
            memset(info, 0, 256);
            sprintf(info, "%sDIR%s", info_s, info_e);
            sprintf(info, "%s%s--%s", info, info_s, info_e);
            build_link(body, dir, cdir->item, info);
        }
    }

    if (fi != 0) {
        pisces_dlist_sort(head_file->pre, head_file->next);
        cfile = head_file->next;
        for (; fi--;) {
            memset(info, 0, 256);

            if (cfile->st_size < 1024) {
                size = cfile->st_size;
                ext = 'B';
            }
            if (KB <= cfile->st_size && cfile->st_size < MB) {
                size = cfile->st_size / KB;
                ext = 'K';
            }
            if (MB <= cfile->st_size && cfile->st_size < GB) {
                size = cfile->st_size / MB;
                ext = 'M';
            }
            if (GB <= cfile->st_size) {
                size = cfile->st_size;
                ext = 'G';
            }

            sprintf(info, "%s%s%s", info_s, strrchr(cfile->item, '.') == NULL ?
                    "???" : strrchr(cfile->item, '.'), info_e);
            sprintf(info, "%s%s%.2f%c%s", info, info_s, size, ext, info_e);
            strftime(date, 25, "%Y-%m-%d %H:%M:%S", localtime_r(&cfile->mtime, &tm));
            sprintf(info, "%s%s%s%s", info, info_s, date, info_e);

            build_link(body, dir, cfile->item, info);
            cfile = cfile->next;
        }
    }

    make_page(body, &page, true);
    abby_response resp;
    resp.confd = sockfd;
    resp.code = 200;
    resp.size = page.size;
    resp.type = "text/html";
    resp.range.offset = -1;
    resp.range.end = -1;
    resp.mtime = 0;

    http_response(resp);
    pisces_dlist_destory(&head_file);
    pisces_dlist_destory(&head_dir);
    closedir(dirp);

    if (write(sockfd, page.html_page, page.size) == -1) {
        free(body);
        pisces_err(sockfd, 404);
        exit(1);
    }
    free(body);
}
